package com.zl.learning.xml;

import com.zl.learning.xml.pojo.Const;
import com.zl.learning.xml.pojo.Form;
import org.apache.log4j.Logger;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * Jaxb2工具类
 *
 * @author zhuc
 * @create 2013-3-29 下午2:40:14
 */
public class JaxbUtil {
    private Logger logger = Logger.getLogger(JaxbUtil.class);
    private static JaxbUtil instance = new JaxbUtil();

    private JaxbUtil() {
    }

    public static JaxbUtil getInstance() {
        return instance;
    }

    private ConcurrentHashMap<String, String> codeMap = new ConcurrentHashMap<>();

    {
        codeMap.put("sex", "DIC_SEX");
        codeMap.put("objectType", "DIC_OBJECT_TYPES");
        codeMap.put("formStatus", "DIC_FORM_STATUS");

        codeMap.put("allowRoles", "DIC_ALLOW_ROLES");
        codeMap.put("scoutTypes", "DIC_SCOUT_TYPES");
        codeMap.put("unMonitorTypes", "DIC_SCOUT_TYPES");

        codeMap.put("department", "DIC_DEPARTMENT");
        codeMap.put("caseType", "DIC_CASE_TYPE");

        codeMap.put("monitorTypes.type", "DIC_MONITOR_TYPES");
    }

    /**
     * JavaBean转换成xml
     * 默认编码UTF-8
     *
     * @param obj
     * @return
     */
    public String convertToXml(Object obj) {
        return convertToXml(obj, "UTF-8");
    }

    private void codeConvert(Object form) {
        codeMap.forEach((k, v) -> {
            Class c = form.getClass();
            try {
                if (k.contains(".")) {
                    String[] _k = k.split("\\.");
                    Field f = c.getDeclaredField(_k[0]);
                    f.setAccessible(true);
                    Object value = f.get(form);
                    if (value != null) {
                        if (value instanceof List) {
                            List list = (List) value;
                            for (Object o : list) {
                                codeConvert(o, _k[1], v);
                            }
                        } else {
                            codeConvert(form, _k[1], v);
                        }
                    }
                    f.setAccessible(false);
                } else {
                    codeConvert(form, k, v);
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                logger.error(e.getMessage(), e);
            }
        });
    }

    private void codeConvert(Object obj, String fieldName, String codeName) throws NoSuchFieldException, IllegalAccessException {
        Class c = obj.getClass();
        Field f = c.getDeclaredField(fieldName);
        f.setAccessible(true);
        Object value = f.get(obj);
        if (value != null) {
            if (value instanceof List) {
                codeConvertList(obj, fieldName, codeName);
            } else {
                codeConvertSingle(obj, fieldName, codeName);
            }
        }
        f.setAccessible(false);
    }

    private void codeConvertSingle(Object form, String filedName, String codeName) {
        Class c = form.getClass();
        try {
            Field f = c.getDeclaredField(filedName);
            f.setAccessible(true);
            String value = (String) f.get(form);
            if (value != null && !value.isEmpty()) {
                String _value = value + "-" + Const.transMap.get(codeName).get(value);
                f.set(form, _value);
            }
            f.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private void codeConvertList(Object form, String filedName, String codeName) {
        Class c = form.getClass();
        try {
            Field f = c.getDeclaredField(filedName);
            f.setAccessible(true);
            List<String> values = (List<String>) f.get(form);
            if (values != null && !values.isEmpty()) {
                List<String> _values = values.stream().map(r -> r + "-" + Const.transMap.get(codeName).get(r)).collect(Collectors.toList());
                f.set(form, _values);
            }
            f.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void unCodeConvert(Object form) {
        codeMap.forEach((k, v) -> {
            Class c = form.getClass();
            try {
                if (k.contains(".")) {
                    String[] _k = k.split("\\.");
                    Field f = c.getDeclaredField(_k[0]);
                    f.setAccessible(true);
                    Object value = f.get(form);
                    if (value != null) {
                        if (value instanceof List) {
                            List list = (List) value;
                            for (Object o : list) {
                                unCodeConvert(o, _k[1]);
                            }
                        } else {
                            unCodeConvert(form, _k[1]);
                        }
                    }
                    f.setAccessible(false);
                } else {
                    unCodeConvert(form, k);
                }
            } catch (NoSuchFieldException | IllegalAccessException e) {
                logger.error(e.getMessage(), e);
            }
        });
    }

    private void unCodeConvert(Object obj, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Class c = obj.getClass();
        Field f = c.getDeclaredField(fieldName);
        f.setAccessible(true);
        Object value = f.get(obj);
        if (value != null) {
            if (value instanceof List) {
                unCodeConvertList(obj, fieldName);
            } else {
                unCodeConvertSingle(obj, fieldName);
            }
        }
        f.setAccessible(false);
    }

    private void unCodeConvertSingle(Object form, String filedName) {
        Class c = form.getClass();
        try {
            Field f = c.getDeclaredField(filedName);
            f.setAccessible(true);
            String value = (String) f.get(form);
            if (value != null && !value.isEmpty()) {
                String _value = value.split("-")[0];
                f.set(form, _value);
            }
            f.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            logger.error(e.getMessage(), e);
        }
    }

    private void unCodeConvertList(Object form, String filedName) {
        Class c = form.getClass();
        try {
            Field f = c.getDeclaredField(filedName);
            f.setAccessible(true);
            List<String> values = (List<String>) f.get(form);
            if (values != null && !values.isEmpty()) {
                List<String> _values = values.stream().map(r -> r.split("-")[0]).collect(Collectors.toList());
                f.set(form, _values);
            }
            f.setAccessible(false);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 把form转成xml
     *
     * @param form
     * @return
     */
    public String convertToFormXml(Form form) {
        codeConvert(form);
        return convertToXml(form, "UTF-8");
    }

    /**
     * 从xml转成form
     *
     * @param xml
     * @return
     */
    public Form converyToForm(String xml) {
        Form form = converyToJavaBean(xml, Form.class);
        unCodeConvert(form);
        return form;
    }

    /**
     * JavaBean转换成xml
     *
     * @param obj
     * @param encoding
     * @return
     */
    public String convertToXml(Object obj, String encoding) {
        String result = null;
        try {
            JAXBContext context = JAXBContext.newInstance(obj.getClass());
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);
            StringWriter writer = new StringWriter();
            marshaller.marshal(obj, writer);
            result = writer.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * xml转换成JavaBean
     *
     * @param xml
     * @param c
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T converyToJavaBean(String xml, Class<T> c) {
        T t = null;
        try {
            JAXBContext context = JAXBContext.newInstance(c);
            Unmarshaller unmarshaller = context.createUnmarshaller();
            t = (T) unmarshaller.unmarshal(new StringReader(xml));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }

}
